package glock

import (
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"log"
	"net"
	"time"
)

// Path: glock/Server.go

type Server struct {
	locker   GLock
	dataSize int
}

func InitMapServer(address string) error {
	// 初始化map锁
	glock := NewGlockMap()

	// 初始化服务
	server := NewServer(glock)

	return server.Start(address)
}

func NewServer(locker GLock) *Server {
	return &Server{
		locker:   locker,
		dataSize: 1024,
	}
}

func (s *Server) Start(address string) error {
	// 1. 监听端口
	listener, err := net.Listen("tcp", address)
	if err != nil {
		return err
	}

	// 2. 接收客户端连接
	for {
		conn, err := listener.Accept()
		if err != nil {
			return err
		}

		// 3. 处理客户端请求
		go s.handleConn(conn)
	}
}

func (s *Server) handleConn(conn net.Conn) {
	defer conn.Close()

	for {

		// read data from conn
		var data = make([]byte, 0, s.dataSize)
		var n int

		// 读取所有数据
		for {
			temp := make([]byte, s.dataSize)
			l, err := conn.Read(temp)

			if err != nil {
				return
			}

			data = append(data, temp[:l]...)

			if l < s.dataSize {
				break
			}

			n += l
		}

		// 解析数据
		cmder, err := parseCmd(data)

		if err != nil {
			s.ErrorRespnse(conn, err)
			return
		}

		// 处理数据
		switch cmder.CmdType {
		case Cmd_Lock:
			var req LockReq

			err = json.Unmarshal(cmder.Datas, &req)

			if err != nil {
				s.ErrorRespnse(conn, errors.Wrap(err, "parse lock req error"))
				return
			}

			// 加锁
			success := s.locker.Lock(req.Key, time.Duration(req.Expire))

			log.Println("lock key:", req.Key, "expire:", req.Expire, "success:", success)

			s.SuccessRespnse(conn, success)

		case Cmd_Unlock:
			var req UnlockReq

			err = json.Unmarshal(cmder.Datas, &req)

			if err != nil {
				s.ErrorRespnse(conn, errors.Wrap(err, "parse unlock req error"))
				return
			}

			// 解锁
			s.locker.Unlock(req.Key)
			s.SuccessRespnse(conn, true)
		}
	}

}

func (s *Server) Respnse(conn net.Conn, result Result) error {
	marshal, err := json.Marshal(result)

	if err != nil {
		return errors.Wrap(err, "marshal result error")
	}

	_, err = conn.Write(marshal)

	return errors.Wrap(err, "write result error")
}

func (s *Server) SuccessRespnse(conn net.Conn, success bool) {
	err := s.Respnse(conn, Result{
		Success: success,
	})

	if err != nil {
		fmt.Println(err)
	}
}

func (s *Server) ErrorRespnse(conn net.Conn, err error) {
	err1 := s.Respnse(conn, Result{
		ErrStr: err.Error(),
	})

	// log
	if err1 != nil {
		fmt.Println(err1)
	}
}

func parseCmd(data []byte) (*Cmder, error) {
	var cmder Cmder

	err := json.Unmarshal(data, &cmder)

	if err != nil {
		log.Println("data:", string(data))
		return nil, errors.Wrap(err, "parse cmd error")
	}

	switch cmder.CmdType {
	case Cmd_Lock:
		return &cmder, nil
	case Cmd_Unlock:
		return &cmder, nil
	default:
		return nil, errors.New("unknown cmd type")
	}
}
